/* eslint-disable class-methods-use-this, no-param-reassign */
import moment from 'moment';
import assert from 'assert';
import Message from '../models/message';
import Channel from '../models/channel';
import ChannelUser from '../models/channelUser';
import Group from '../models/group';
import Mention from '../models/mention';
import robotMessageDispatcher from '../robots';
import waitHandler from '../handler/waitHandler';

import componentContainer from '../../components/componentContainer';

class MessageHandler {
  /**
   * 向指定channelId发送信息
   * @param {string} channelId
   * @param {object} user
   * @param {object} input
   * type input = {
   *  body : "232323"
   *  channel : "598b1121b1fd4cda514e21ba"
   *  fromUser : "58fdbbf0b4f40a7f58b3d43e"
   *  mention : ""
   *  fromSocialId : ""
   *  toSocialId: ""
   *  subType : "text"
   *  token : "xkj6clZRKSGqAdIAFpwhaHDKeLbPdL1wSMAeR2N0"
   * }
   */
  async post(channelId, user, input) {
    const channel = await Channel.findById(channelId);
    assert(channel, 'channel not found');
    const channelUser = ChannelUser.findOne({ channel, user });
    assert(channelUser || user.isRobot, 'Permission denied');

    const message = await this.createMessage(channelId, user, input);

    const { mention, fromSocialId, toSocialId } = input;
    await this.createMention(message, mention);

    await this.sendWeChatMessage(toSocialId, message);

    await this.updateChannelLastActive(channel);
    await this.updateChannelUserUnread(channel);
    await this.updateChannelUserLastMessage(channelUser, message, fromSocialId);

    await this.notifyWaitList(channel, user);
    await this.notifyRobot(message, user);
    this.publishMessage(message, channel, user);

    return message;
  }

  async sendWeChatMessage(toSocialId, message) {
    if (!toSocialId) return Promise.resolve();
    const apiBridge = componentContainer.getApiBridge();

    try {
      await apiBridge.sendWeChatMessage({
        toSocialId,
        content: message.body.replace(/^@[\S\s]+?:/, ''),
        type: message.subType,
      });
      message.isSent = 1;
    } catch (e) {
      message.isSent = -1;
    }

    return message.save();
  }

  async createMessage(channelId, user, input) {
    const now = new Date();
    const message = new Message({
      fromUser: user,
      toChannel: channelId,
      subType: input.subType,
      body: input.body,
      mentions: [],
      fromSocialId: input.fromSocialId || null,
      createdAt: now,
      updatedAt: now,
    });

    return message.save();
  }

  async createMention(message, inputMention) {
    if (!inputMention) return Promise.resolve();

    const mention = new Mention({
      message,
      toUser: inputMention,
    });

    await mention.save();
    message.mentions.push(mention);

    await message.save();

    return mention;
  }

  notifyRobot(message, user) {
    if (user.isRobot) return Promise.resolve();
    return robotMessageDispatcher.dispatchEvent(message);
  }

  async notifyWaitList(channel, user) {
    const customerGroup = await Group.findById(user.group);
    if (user.isRobot || !customerGroup.isCustomer) return;
    const inService = await channel.isInService(user);

    if (!inService) {
      await waitHandler.add(channel, customerGroup);
    }
  }

  async updateChannelLastActive(channel) {
    channel.lastActiveAt = moment.now(); // eslint-disable-line

    return channel.save();
  }

  async updateChannelUserUnread(channel) {
    return ChannelUser.update({ channel }, { $inc: { unreadCount: 1 } }, { multi: true });
  }

  async updateChannelUserLastMessage(channelUser, message, fromSocialId) {
    const updateProps = {
      lastMessage: message,
      lastActiveAt: moment.now(),
      unreadCount: 0,
    };
    if (fromSocialId) updateProps.lastActiveSocialId = fromSocialId;

    return channelUser.update(updateProps);
  }

  async publishMessage(message, channel, user) {
    const redis = componentContainer.getRedis();
    const messagePayload = {
      id: message.id,
      createdAt: message.createdAt,
      subType: message.subType,
      body: message.body,
      user: {
        id: user.id,
        username: user.username,
        nickname: user.nickname || '',
        headImgUrl: user.headImgUrl,
      },
      fromSocialId: message.fromSocialId,
      channel: {
        id: channel.id,
        name: channel.name,
      },
      isSent: message.isSent,
      mentions: message.mentions,
      messageFromCustomer: user.isCustomer(),
    };

    redis.publish(`channel:${channel.id}`, JSON.stringify(messagePayload));
  }
}

export default new MessageHandler();
